﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace Gmantis.Controls.Toolbar
{
    internal interface IToolbarControl
    {
        // Methods
        void ChangeState(string state);
        void FireClick();
    }

    public class ToolbarToggleButton : ToggleButton, IToolbarControl
    {
        // Fields
        private static Dictionary<string, List<WeakReference>> _groupNameToElements;
        private string _state;
        public static readonly DependencyProperty GroupNameProperty = DependencyProperty.Register("GroupName", typeof(string), typeof(ToolbarToggleButton), new PropertyMetadata(null, new PropertyChangedCallback(ToolbarToggleButton.OnGroupNameChanged)));
        public static readonly DependencyProperty LabelTitleProperty = DependencyProperty.Register("LabelTitle", typeof(string), typeof(ToolbarToggleButton), new PropertyMetadata("", new PropertyChangedCallback(ToolbarToggleButton.OnButtonChanged)));
        public static readonly DependencyProperty LargeImageSourceProperty = DependencyProperty.Register("LargeImageSource", typeof(ImageSource), typeof(ToolbarToggleButton), new PropertyMetadata(null, new PropertyChangedCallback(ToolbarToggleButton.OnButtonChanged)));
        public static readonly DependencyProperty MouseOverBrushProperty = DependencyProperty.Register("MouseOverBrush", typeof(Brush), typeof(ToolbarToggleButton), null);
        public static readonly DependencyProperty PressedBrushProperty = DependencyProperty.Register("PressedBrush", typeof(Brush), typeof(ToolbarToggleButton), null);
        public static readonly DependencyProperty SmallImageSourceProperty = DependencyProperty.Register("SmallImageSource", typeof(ImageSource), typeof(ToolbarToggleButton), new PropertyMetadata(null, new PropertyChangedCallback(ToolbarToggleButton.OnButtonChanged)));

        // Methods
        public ToolbarToggleButton()
        {
            base.DefaultStyleKey = typeof(ToolbarToggleButton);
            SetCustomDefaultValues();
        }

        void IToolbarControl.ChangeState(string state)
        {
            this._state = ButtonHelper.GetState(state, this.LargeImageSource, this.SmallImageSource, this.LabelTitle);
            base.InvalidateMeasure();
        }

        void IToolbarControl.FireClick()
        {
            this.OnClick();
        }

        private static DependencyObject GetParent(DependencyObject obj)
        {
            DependencyObject parent = null;
            while (true)
            {
                FrameworkElement element = obj as FrameworkElement;
                if ((element == null) || (element.Parent == null))
                {
                    return parent;
                }
                parent = element.Parent;
                obj = parent;
            }
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            if (!string.IsNullOrEmpty(this._state))
            {
                VisualStateManager.GoToState(this, this._state, false);
                this._state = null;
            }
            else
            {
                VisualStateManager.GoToState(this, ButtonHelper.GetDefaultState(this.LargeImageSource, this.SmallImageSource, this.LabelTitle), false);
            }
            return base.MeasureOverride(availableSize);
        }

        private static void OnButtonChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            ((FrameworkElement)obj).InvalidateMeasure();
        }

        private static void OnGroupNameChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            ToolbarToggleButton button = sender as ToolbarToggleButton;
            string oldValue = (string)args.OldValue;
            Unregister(oldValue, button);
            string newValue = (string)args.NewValue;
            Register(newValue, button);
        }

        protected override void OnToggle()
        {
            this.UpdateRadioButtonGroup();
            if ((base.IsChecked != true) || string.IsNullOrEmpty(this.GroupName))
            {
                base.OnToggle();
            }
        }

        private static void PurgeExpiredReferences(List<WeakReference> elements, object elementToRemove)
        {
            int index = 0;
            while (index < elements.Count)
            {
                object target = elements[index].Target;
                if ((target == null) || (target == elementToRemove))
                {
                    elements.RemoveAt(index);
                }
                else
                {
                    index++;
                }
            }
        }

        private static void Register(string groupName, ToolbarToggleButton button)
        {
            if (_groupNameToElements == null)
            {
                _groupNameToElements = new Dictionary<string, List<WeakReference>>(1);
            }
            if (!string.IsNullOrEmpty(groupName))
            {
                List<WeakReference> list = null;
                if (!_groupNameToElements.TryGetValue(groupName, out list) || (list == null))
                {
                    list = new List<WeakReference>(1);
                    _groupNameToElements[groupName] = list;
                }
                else
                {
                    PurgeExpiredReferences(list, null);
                }
                list.Add(new WeakReference(button));
            }
        }

        private void SetCustomDefaultValues()
        {
            Register("", this);
        }

        private static void Unregister(string groupName, ToolbarToggleButton button)
        {
            List<WeakReference> list;
            if (groupName == null)
            {
                groupName = "";
            }
            if (((_groupNameToElements != null) && _groupNameToElements.TryGetValue(groupName, out list)) && (list != null))
            {
                PurgeExpiredReferences(list, button);
                if (list.Count == 0)
                {
                    _groupNameToElements.Remove(groupName);
                }
            }
        }

        private void UpdateRadioButtonGroup()
        {
            string groupName = this.GroupName;
            if (!string.IsNullOrEmpty(groupName))
            {
                DependencyObject parent = GetParent(this);
                List<WeakReference> list = _groupNameToElements[groupName];
                int index = 0;
                while (index < list.Count)
                {
                    ToolbarToggleButton target = list[index].Target as ToolbarToggleButton;
                    if (target == null)
                    {
                        list.RemoveAt(index);
                    }
                    else
                    {
                        if ((target != this) && ((target.IsChecked == true) && (parent == GetParent(target))))
                        {
                            target.IsChecked = false;
                        }
                        index++;
                    }
                }
            }
        }

        // Properties
        public string GroupName
        {
            get { return (string)GetValue(GroupNameProperty); }
            set { SetValue(GroupNameProperty, value); }
        }

        public string LabelTitle
        {
            get { return (string)GetValue(LabelTitleProperty); }
            set { SetValue(LabelTitleProperty, value); }
        }

        public ImageSource LargeImageSource
        {
            get { return (ImageSource)GetValue(LargeImageSourceProperty); }
            set { SetValue(LargeImageSourceProperty, value); }
        }

        public Brush MouseOverBrush
        {
            get { return (Brush)GetValue(MouseOverBrushProperty); }
            set { SetValue(MouseOverBrushProperty, value); }
        }

        public Brush PressedBrush
        {
            get { return (Brush)GetValue(PressedBrushProperty); }
            set { SetValue(PressedBrushProperty, value); }
        }

        public ImageSource SmallImageSource
        {
            get { return (ImageSource)GetValue(SmallImageSourceProperty); }
            set { SetValue(SmallImageSourceProperty, value); }
        }
    }
}
